/*
 * @author: liubing
 * @Date: 2020-12-13 22:46:24
 * @desc: 
 */
"use strict";

const vsSource = `
 attribute vec3 aVertexPosition;
 attribute vec4 aVertexColor;

 uniform mat4 uModelViewMatrix;
 uniform mat4 uProjectionMatrix;
 varying vec4 vColor;

 void main(){
     gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aVertexPosition, 1.0);
     vColor = aVertexColor;
 }`;


const fsSource = `
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 vColor;
void main() {
    gl_FragColor= vec4(0.5,0.5,0.5,1);
}`;


let g_Buffers = null;
let g_ProgramInfo = null;
var g_Texture = null;


var cubeRotation = 0.0;
var then = 0;
var device; // gl 设备

var program;
var program_ground;
var programGroundInfo;

function main() {

    var canvas = document.querySelector("#canvas");
    var gl = canvas.getContext("webgl");
    if (!gl) {
        return;
    }

    const shaderProgramGround = initShaderProgram(gl,vsSource,fsSource);
    programGroundInfo = {
        program: shaderProgramGround,
        attribLocations: {
            vertexPosition: gl.getAttribLocation(shaderProgramGround, 'aVertexPosition'),
            vertexColor:gl.getAttribLocation(shaderProgramGround,"aVertexColor"),
        },
        uniformLocations: {
            projectionMatrix: gl.getUniformLocation(shaderProgramGround, 'uProjectionMatrix'),
            modelViewMatrix: gl.getUniformLocation(shaderProgramGround, 'uModelViewMatrix'),
        },
    };


    // setup GLSL program
    program = webglUtils.createProgramFromScripts(gl, ["vertex-shader-3d", "fragment-shader-3d"]);
    

    // look up where the vertex data needs to go.
    var positionLocation = gl.getAttribLocation(program, "a_position");
    var normalLocation = gl.getAttribLocation(program, "a_normal");
    var textureCoordLocation = gl.getAttribLocation(program, "aTextureCoord");


    // lookup uniforms
    var worldViewProjectionLocation = gl.getUniformLocation(program, "u_worldViewProjection");
    var worldInverseTransposeLocation = gl.getUniformLocation(program, "u_worldInverseTranspose");

    var uSampler = gl.getUniformLocation(program, "uSampler");

    var shininessLocation = gl.getUniformLocation(program, "u_shininess");
    var lightDirectionLocation = gl.getUniformLocation(program, "u_lightDirection");
    var innerLimitLocation = gl.getUniformLocation(program, "u_innerLimit");
    var outerLimitLocation = gl.getUniformLocation(program, "u_outerLimit");
    var lightWorldPositionLocation = gl.getUniformLocation(program, "u_lightWorldPosition");
    var viewWorldPositionLocation = gl.getUniformLocation(program, "u_viewWorldPosition");
    var worldLocation = gl.getUniformLocation(program, "u_world");

    let buffers = initBuffer(gl);
    var positionBuffer = buffers.positionBuffer;
    var normalBuffer = buffers.normalBuffer;
    var textureCoordBuffer = buffers.textureCoordBuffer;
    var indexBuffer = buffers.indexBuffer;



    function radToDeg(r) {
        return r * 180 / Math.PI;
    }

    function degToRad(d) {
        return d * Math.PI / 180;
    }

    var fieldOfViewRadians = degToRad(60);
    var fRotationRadians = 0;
    var shininess = 150;
    var lightRotationX = 0;
    var lightRotationY = 0;
    var lightDirection = [0.5, 0.5, 1]; // this is computed in updateScene
    var innerLimit = degToRad(10);
    var outerLimit = degToRad(30);
    var fCameraX = 0;
    var fCameraY = 150;
    var fCameraZ = 200;
    var lightPositionX = 40;
    var lightPositionY = 60;
    var lightPositionZ = 120;

    loadTexture(gl, 'resources/concrete1.jpg');
    //   drawScene();

    webglLessonsUI.setupSlider("#fRotation", {
        value: radToDeg(fRotationRadians),
        slide: updateRotation,
        min: -360,
        max: 360
    });
    webglLessonsUI.setupSlider("#lightRotationX", {
        value: lightRotationX,
        slide: updatelightRotationX,
        min: -2,
        max: 2,
        precision: 2,
        step: 0.001
    });
    webglLessonsUI.setupSlider("#lightRotationY", {
        value: lightRotationY,
        slide: updatelightRotationY,
        min: -2,
        max: 2,
        precision: 2,
        step: 0.001
    });
    webglLessonsUI.setupSlider("#innerLimit", {
        value: radToDeg(innerLimit),
        slide: updateInnerLimit,
        min: 0,
        max: 180
    });
    webglLessonsUI.setupSlider("#outerLimit", {
        value: radToDeg(outerLimit),
        slide: updateOuterLimit,
        min: 0,
        max: 180
    });
    webglLessonsUI.setupSlider("#fCameraX", {
        value: fCameraX,
        slide: updateCameraX,
        max: 400,
        min: -400,
        step: 1,
        precision: 2
    });
    webglLessonsUI.setupSlider("#fCameraY", {
        value: fCameraY,
        slide: updateCameraY,
        max: 400,
        min: -400,
        step: 1,
        precision: 2
    });
    webglLessonsUI.setupSlider("#fCameraZ", {
        value: fCameraZ,
        slide: updateCameraZ,
        max: 400,
        min: 10,
        step: 1,
        precision: 2
    });

    webglLessonsUI.setupSlider("#lightPositionX", {
        value: lightPositionX,
        slide: updateLightPositionX,
        max: 400,
        min: -400,
        step: 1,
        precision: 2
    });
    webglLessonsUI.setupSlider("#lightPositionY", {
        value: lightPositionY,
        slide: updateLightPositionY,
        max: 400,
        min: -400,
        step: 1,
        precision: 2
    });
    webglLessonsUI.setupSlider("#lightPositionZ", {
        value: lightPositionZ,
        slide: updateLightPositionZ,
        max: 400,
        min: -400,
        step: 1,
        precision: 2
    });

    function updateRotation(event, ui) {
        fRotationRadians = degToRad(ui.value);
        drawScene();
    }

    function updatelightRotationX(event, ui) {
        lightRotationX = ui.value;
        drawScene();
    }

    function updatelightRotationY(event, ui) {
        lightRotationY = ui.value;
        drawScene();
    }

    function updateInnerLimit(event, ui) {
        innerLimit = degToRad(ui.value);
        drawScene();
    }

    function updateOuterLimit(event, ui) {
        outerLimit = degToRad(ui.value);
        drawScene();
    }

    function updateCameraX(event, ui) {
        fCameraX = ui.value;
        drawScene();

    }

    function updateCameraY(event, ui) {
        fCameraY = ui.value;
        drawScene();

    }

    function updateCameraZ(event, ui) {
        fCameraZ = ui.value;
        drawScene();
    }


    function updateLightPositionX(event, ui) {
        lightPositionX = ui.value;
        drawScene();
    }

    function updateLightPositionY(event, ui) {
        lightPositionY = ui.value;
        drawScene();
    }

    function updateLightPositionZ(event, ui) {
        lightPositionZ = ui.value;
        drawScene();
    }

    function drawGround(){
        console.log("drawGround() -> ");
    }

    // Draw the scene.
    function drawScene() {
        console.log("drawScene");
        webglUtils.resizeCanvasToDisplaySize(gl.canvas);

        // Tell WebGL how to convert from clip space to pixels
        gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

        // Clear the canvas AND the depth buffer.
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

        // Turn on culling. By default backfacing triangles
        // will be culled.
        // gl.enable(gl.CULL_FACE);
        gl.enable(gl.DEPTH_TEST);

        gl.useProgram(programGroundInfo.program);

        drawGround();
        // Enable the depth buffer
        

        // Tell it to use our program (pair of shaders)
        gl.useProgram(program);

        // Turn on the position attribute


        // Bind the position buffer.
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

        console.log('positionLocation', positionLocation)

        // Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER)
        var size = 3; // 3 components per iteration
        var type = gl.FLOAT; // the data is 32bit floats
        var normalize = false; // don't normalize the data
        var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
        var offset = 0; // start at the beginning of the buffer
        gl.vertexAttribPointer(positionLocation, size, type, normalize, stride, offset);
        gl.enableVertexAttribArray(positionLocation);

        // // Turn on the normal attribute
        gl.enableVertexAttribArray(normalLocation);

        // // Bind the normal buffer.
        gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);

        // Tell the attribute how to get data out of normalBuffer (ARRAY_BUFFER)
        var size = 3; // 3 components per iteration
        var type = gl.FLOAT; // the data is 32bit floating point values
        var normalize = false; // normalize the data (convert from 0-255 to 0-1)
        var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
        var offset = 0; // start at the beginning of the buffer
        gl.vertexAttribPointer(normalLocation, size, type, normalize, stride, offset);


        gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer);
        gl.vertexAttribPointer(textureCoordLocation, 2, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(textureCoordLocation);


        // Compute the projection matrix
        var aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
        var zNear = 1;
        var zFar = 2000;
        var projectionMatrix = m4.perspective(fieldOfViewRadians, aspect, zNear, zFar);

        // Compute the camera's matrix
        var camera = [fCameraX, fCameraY, fCameraZ];
        // var camera = [0, 10, 10];
        var target = [0, 35, 0];
        var up = [0, 1, 0];
        var cameraMatrix = m4.lookAt(camera, target, up);

        // Make a view matrix from the camera matrix.
        var viewMatrix = m4.inverse(cameraMatrix);

        // Compute a view projection matrix
        var viewProjectionMatrix = m4.multiply(projectionMatrix, viewMatrix);

        // Draw a F at the origin
        var worldMatrix = m4.yRotation(fRotationRadians);

        // Multiply the matrices.
        var worldViewProjectionMatrix = m4.multiply(viewProjectionMatrix, worldMatrix);
        var worldInverseMatrix = m4.inverse(worldMatrix);
        var worldInverseTransposeMatrix = m4.transpose(worldInverseMatrix);

        // Set the matrices
        gl.uniformMatrix4fv(worldViewProjectionLocation, false, worldViewProjectionMatrix);
        gl.uniformMatrix4fv(worldInverseTransposeLocation, false, worldInverseTransposeMatrix);
        gl.uniformMatrix4fv(worldLocation, false, worldMatrix);

        // Set the color to use
        // gl.uniform4fv(colorLocation, [0.2, 1, 0.2, 1]); // green

        // set the light position
        // const lightPosition = [40, 60, 120];
        gl.uniform3fv(lightWorldPositionLocation, [lightPositionX,lightPositionY,lightPositionZ]);

        // set the camera/view position
        gl.uniform3fv(viewWorldPositionLocation, camera);

        // set the shininess
        gl.uniform1f(shininessLocation, shininess);

        // set the spotlight uniforms

        // since we don't have a plane like most spotlight examples
        // let's point the spot light at the F
        {
            var lmat = m4.lookAt([lightPositionX,lightPositionY,lightPositionZ], target, up);
            lmat = m4.multiply(m4.xRotation(lightRotationX), lmat);  // 绕着x轴旋转
            lmat = m4.multiply(m4.yRotation(lightRotationY), lmat); //绕着Y轴旋转
            // get the zAxis from the matrix
            // negate it because lookAt looks down the -Z axis
            lightDirection = [-lmat[8], -lmat[9], -lmat[10]];
        }

        gl.uniform3fv(lightDirectionLocation, lightDirection);
        gl.uniform1f(innerLimitLocation, Math.cos(innerLimit));
        gl.uniform1f(outerLimitLocation, Math.cos(outerLimit));

        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, g_Texture);
        gl.uniform1i(uSampler, 0);
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
        gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
    }

    function loadTexture(gl, url) {

        const texture = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 255, 255]))

        const image = new Image();
        image.src = url;
        image.onload = function () {
            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
            if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
                gl.generateMipmap(gl.TEXTURE_2D);
            } else {
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
            }
            g_Texture = texture
            drawScene()
        }

        return texture;
    }
}

function initBuffer(gl) {

    var positions = [

        -60.0, -60.0, 60.0,
        60.0, -60.0, 60.0,
        60.0, 60.0, 60.0,
        -60.0, 60.0, 60.0,
        // Back face
        -60.0, -60.0, -60.0,
        -60.0, 60.0, -60.0,
        60.0, 60.0, -60.0,
        60.0, -60.0, -60.0,

        // Top face
        -60.0, 60.0, -60.0,
        -60.0, 60.0, 60.0,
        60.0, 60.0, 60.0,
        60.0, 60.0, -60.0,

        // Bottom face
        -60.0, -60.0, -60.0,
        -60.0, -60.0, 60.0,
        60.0, -60.0, 60.0,
        60.0, -60.0, -60.0,


        // Right face X 位置不变
        60.0, -60.0, -60.0,
        60.0, 60.0, -60.0,
        60.0, 60.0, 60.0,
        60.0, -60.0, 60.0,

        //LEFT
        -60.0, -60.0, -60.0,
        -60.0, 60.0, -60.0,
        -60.0, 60.0, 60.0,
        -60.0, -60.0, 60.0,
    ]

    var positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);


    var vertexNormals = [
        // Front
        0.0, 0.0, 1.0,
        0.0, 0.0, 1.0,
        0.0, 0.0, 1.0,
        0.0, 0.0, 1.0,

        // Back
        0.0, 0.0, -1.0,
        0.0, 0.0, -1.0,
        0.0, 0.0, -1.0,
        0.0, 0.0, -1.0,

        // Top
        0.0, 1.0, 0.0,
        0.0, 1.0, 0.0,
        0.0, 1.0, 0.0,
        0.0, 1.0, 0.0,

        // Bottom
        0.0, -1.0, 0.0,
        0.0, -1.0, 0.0,
        0.0, -1.0, 0.0,
        0.0, -1.0, 0.0,

        // Right
        1.0, 0.0, 0.0,
        1.0, 0.0, 0.0,
        1.0, 0.0, 0.0,
        1.0, 0.0, 0.0,

        // Left
        -1.0, 0.0, 0.0,
        -1.0, 0.0, 0.0,
        -1.0, 0.0, 0.0,
        -1.0, 0.0, 0.0
    ];
    var normalBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexNormals), gl.STATIC_DRAW);

    const textureCoords = [
        //front
        0.0, 0.0,
        1.0, 0.0,
        1.0, 1.0,
        0.0, 1.0,

        //back
        0.0, 0.0,
        1.0, 0.0,
        1.0, 1.0,
        0.0, 1.0,

        //left
        0.0, 0.0,
        1.0, 0.0,
        1.0, 1.0,
        0.0, 1.0,
        //right
        0.0, 0.0,
        1.0, 0.0,
        1.0, 1.0,
        0.0, 1.0,

        //top
        0.0, 0.0,
        1.0, 0.0,
        1.0, 1.0,
        0.0, 1.0,

        //bottom
        0.0, 0.0,
        1.0, 0.0,
        1.0, 1.0,
        0.0, 1.0,
    ];
    var textureCoordBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);



    const indices = [
        0, 1, 2, 0, 2, 3, //
        4, 5, 6, 4, 6, 7, //
        8, 9, 10, 8, 10, 11,
        12, 13, 14, 12, 14, 15, //
        16, 17, 18, 16, 18, 19, //
        20, 21, 22, 20, 22, 23, //
    ];
    var indexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Int16Array(indices), gl.STATIC_DRAW);

    return {
        positionBuffer: positionBuffer,
        textureCoordBuffer: textureCoordBuffer,
        normalBuffer: normalBuffer,
        indexBuffer: indexBuffer,
    };
}


function initGround(gl){
    
    var positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

    var colorBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER,colorBuffer);
    gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(colorBuffer),gl.STATIC_DRAW);

    return  {
        position:positionBuffer
    }
}

main();